#pragma once

#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <cstring>
#include "log.hpp"
#include "Fail.hpp"

using namespace std;

static const int default_sock = -1;
static const int backlog = 32;

class Sock
{
public: 
    //默认构造
    Sock(): _sock(default_sock)
    {}

    //socket
    void Socket()
    {
        _sock = socket(AF_INET, SOCK_STREAM, 0);
        if(_sock < 0)
        {
            log_message(Fatal, "socket fail, errno: %d, strerror: %s", errno, strerror(errno));
            exit(SOCKET_FAIL);
        }
    }

    //bind
    void Bind(const uint16_t& port)
    {
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(port);
        local.sin_addr.s_addr = INADDR_ANY;
        if(bind(_sock, (struct sockaddr*)&local, sizeof(local)) < 0)
        {
            log_message(Fatal, "bind fail, errno: %d, strerror: %s", errno, strerror(errno));
            exit(BIND_FAIL);
        }
    }

    //listen
    void Listen()
    {
        if(listen(_sock, backlog) < 0)
        {
            log_message(Fatal, "listen fail, errno: %d, strerror: %s", errno, strerror(errno));
            exit(LISTEN_FAIL);
        }
    }

    //accept
    int Accept(string& client_ip, uint16_t& client_port, int& err)
    {
        struct sockaddr_in client;
        memset(&client, 0, sizeof(client));
        socklen_t len = sizeof(client);
        int sock = accept(_sock, (struct sockaddr*)&client, &len);
        err = errno;
        if(sock < 0)
        {
            log_message(Warning, "accept fail, errno: %d, strerror: %s", errno, strerror(errno));
        }
        else
        {
            client_ip = inet_ntoa(client.sin_addr);
            client_port = ntohs(client.sin_port);
        }

        return sock;
    }

    //connect
    void Connect(const string& server_ip, const uint16_t& server_port)
    {
        struct sockaddr_in server;
        memset(&server, 0, sizeof(server));
        server.sin_family = AF_INET;
        server.sin_addr.s_addr = inet_addr(server_ip.c_str());
        server.sin_port = htons(server_port);

        int cnt = 5;
        while(connect(_sock, (struct sockaddr*)&server, sizeof(server)) < 0 && cnt > 0)
        {
            log_message(Debug, "正在尝试重新连接,重连次数还剩: %d", cnt--);
        }
        if(cnt == 0)
        {
            log_message(Warning, "connect fail, errno: %d, strerror: %s", errno, strerror(errno));
            exit(CONNECT_FAIL);
        }
    }

    int GetSock()
    {
        return _sock;
    }

    //析构
    ~Sock()
    {
        if(_sock != default_sock)
        {
            close(_sock);
        }
    }
private:
    int _sock;
};